﻿//-----------------------------------------------------------------------
// <copyright file="CharacterClassRepository.cs" company="FastNET Services">
//     Copyright (c) 2008-2009. All Rights Reserved.
// </copyright>
// <license>
//     GPLv2
// </license>
// <warranty>
//     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
//     BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
//     DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// </warranty>
//-----------------------------------------------------------------------
#region Usings

using System;
using System.Collections.Generic;
using System.Data.EntityClient;
using System.Linq;
using GateShift.Common.Repository;
using GateShift.Common.Repository.Interfaces.DataInterfaces;
using GateShift.Common.Repository.Interfaces.Repositories;
using GateShift.Services.GameData.Entities;

#endregion

namespace GateShift.Services.GameData.Repositories
{
    /// <summary>
    /// General repository for managing CharacterClass
    /// </summary>
    internal class CharacterClassRepository : ICharacterClassRepository
    {
        #region CTor/DTor

        /// <summary>
        /// Initializes a new instance of the CharacterClassRepository class
        /// </summary>
        /// <param name="connection">Reference to the common EntityConnection for the repository</param>
        internal CharacterClassRepository(EntityConnection connection)
        {
            Connection = connection;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets a reference to the common EntityConnection for the repository
        /// </summary>
        protected EntityConnection Connection { get; private set; }

        #endregion

        /// <summary>
        /// Retrieves all instances of ICharacterClassData from the repository
        /// </summary>
        /// <returns>List of the requested ICharacterClassData</returns>
        public IList<ICharacterClassData> Retrieve()
        {
            List<ICharacterClassData> returnValues = new List<ICharacterClassData>();

            using (EntityDataAdapter dataSource = new EntityDataAdapter(Connection))
            {
                foreach (CharacterClass characterClass in from item in dataSource.CharacterClasses select item)
                {
                    returnValues.Add(characterClass);
                    dataSource.Detach(characterClass);
                }
            }

            return returnValues;
        }

        /// <summary>
        /// Deletes the requested ICharacterClassData
        /// </summary>
        /// <param name="data">ICharacterClassData to delete from the repository</param>
        /// <exception cref="ArgumentNullException">Thrown if data is null</exception>
        /// <exception cref="InvalidCastException">Thrown if the data is of the wrong type for this datasource</exception>
        /// <exception cref="InvalidOperationException">Thrown if the ICharacterClassData is new or already deleted</exception>
        public void Delete(ICharacterClassData data)
        {
            if (data == null)
            {
                throw new ArgumentNullException("data", "Must have a valid data to delete");
            }

            if (!(data is CharacterClass))
            {
                throw new InvalidCastException("data is not of type CharacterClass. Cannot delete this object through this repository");
            }

            if (data.IsNew)
            {
                throw new InvalidOperationException("data cannot be new before deleting. There is nothing to delete.");
            }

            if (data.IsDeleted)
            {
                throw new InvalidOperationException("data already deleted.");
            }

            using (EntityDataAdapter dataSource = new EntityDataAdapter(Connection))
            {
                dataSource.Attach((CharacterClass)data);
                dataSource.DeleteObject(data);
                dataSource.SaveChanges();
            }
        }

        /// <summary>
        /// Saves the requested ICharacterClassData
        /// </summary>
        /// <param name="data">ICharacterClassData to save to the repository</param>
        /// <exception cref="ArgumentNullException">Thrown if data is null</exception>
        /// <exception cref="InvalidCastException">Thrown if the data is of the wrong type for this datasource</exception>
        public void Save(ICharacterClassData data)
        {
            if (data == null)
            {
                throw new ArgumentNullException("data", "Must have a valid data to delete");
            }

            if (!(data is CharacterClass))
            {
                throw new InvalidCastException("data is not of type CharacterClass. Cannot save this object through this repository");
            }

            using (EntityDataAdapter dataSource = new EntityDataAdapter(Connection))
            {
                if (!data.IsDirty)
                {
                    return;
                }

                if (data.IsNew)
                {
                    dataSource.AddToCharacterClasses((CharacterClass)data);
                    dataSource.SaveChanges();
                    dataSource.Detach(data);
                }
                else
                {
                    dataSource.ApplyPropertyChanges("CharacterClasses", data);
                    dataSource.SaveChanges();
                }
            }
        }

        /// <summary>
        /// Creates a new instance of ICharacterClassData
        /// </summary>
        /// <returns>New Instance of ICharacterClassData</returns>
        public ICharacterClassData Create()
        {
            return new CharacterClass();
        }

        /// <summary>
        /// Retrieves all character class mappings by character ID
        /// </summary>
        /// <param name="characterID">Character to retrieve associated classes for</param>
        /// <returns>List of ICharacterClassData with all matching results</returns>
        public IList<ICharacterClassData> Retrieve(Identity characterID)
        {
            if (!characterID.CanFitInGuid())
            {
                throw new InvalidCastException(string.Format("Identity for this type of repository requires it to be of Guid Type. Currently is {0}.", characterID.UnderlyingType()));
            }

            // This has to be done here because LINQ does not support the inline case
            Guid searchID = characterID;

            List<ICharacterClassData> returnValues = new List<ICharacterClassData>();

            using (EntityDataAdapter dataSource = new EntityDataAdapter(Connection))
            {
                IEnumerable<CharacterClass> query = from
                                                            characterClass
                                                            in
                                                        dataSource.CharacterClasses
                                                    where
                                                        characterClass.CharacterID == searchID
                                                    select
                                                        characterClass;

                foreach (CharacterClass characterClass in query)
                {
                    returnValues.Add(characterClass);
                    dataSource.Detach(characterClass);
                }

                return returnValues;
            }
        }

        /// <summary>
        /// Retrieves character class mapping by character ID and class ID
        /// </summary>
        /// <param name="characterID">Character to retrieve associated mapping by</param>
        /// <param name="classID">Class to retrieve associated mapping by</param>
        /// <returns>ICharacterClassData with matching result or null if none was found</returns>
        public ICharacterClassData Retrieve(Identity characterID, Identity classID)
        {
            if (!characterID.CanFitInGuid())
            {
                throw new InvalidCastException(string.Format("Character identity for this type of repository requires it to be of Guid Type. Currently is {0}.", characterID.UnderlyingType()));
            }

            if (!classID.CanFitInInt16())
            {
                throw new InvalidCastException(string.Format("Class identity for this type of repository requires it to be of short  Type. Currently is {0}.", classID.UnderlyingType()));
            }

            // This has to be done here because LINQ does not support the inline case
            Guid searchCharacterID = characterID;
            long searchClassID = classID;

            using (EntityDataAdapter dataSource = new EntityDataAdapter(Connection))
            {
                CharacterClass foundCharacterClass = (from
                                                             characterClass
                                                             in
                                                          dataSource.CharacterClasses
                                                      where
                                                          characterClass.CharacterID == searchCharacterID &&
                                                          characterClass.ClassID == searchClassID
                                                      select
                                                          characterClass).FirstOrDefault();

                if (foundCharacterClass != null)
                {
                    dataSource.Detach(foundCharacterClass);
                }

                return foundCharacterClass;
            }
        }
    }
}